package com.wbt.algorithm;

public class BaesSort {

    public static void main(String[] args) {
        int arr[] = {6, 3, 8, 2, 9, 1, 7, 5};

        //bubbleSort(arr);
        quickSort(arr,0,arr.length-1);
        // 结果输出
       printArr(arr);

    }


    /**
     * 冒泡排序
     * a、冒泡排序，是通过每一次遍历获取最大/最小值
     * 　　b、将最大值/最小值放在尾部/头部
     * 　　c、然后除开最大值/最小值，剩下的数据在进行遍历获取最大/最小值
     */
    private static int[] bubbleSort(int[] arr) {
        for (int i = 0; i < arr.length - 1; i++) {
            for (int j = i + 1; j < arr.length; j++) {
                // 内层循环，升序（如果前一个值比后一个值大，则交换）
                // 内层循环一次，获取一个最大值
                if (arr[j] > arr[i]) {
                    int tmp = arr[i];
                    arr[i] = arr[j];
                    arr[j] = tmp;
                }
            }
        }
        return arr;
    }


    /**
     * 选择排序
     * 　　a、将第一个值看成最小值
     * 　　b、然后和后续的比较找出最小值和下标
     * 　　c、交换本次遍历的起始值和最小值
     * 　　d、说明：每次遍历的时候，将前面找出的最小值，看成一个有序的列表，后面的看成无序的列表，然后每次遍历无序列表找出最小值。
     */
    public static int[] selectSort(int[] arr) {
        for (int i = 0; i < arr.length; i++) {
            int min = arr[i];
            int index = i;
            for (int j = i + 1; j < arr.length; j++) {
                if (min > arr[j]) {
                    min = arr[j];
                    index = j;
                }
            }
            int tmp = arr[i];
            arr[i] = min;
            arr[index] = tmp;
        }
        return arr;
    }

    /**
     * 插入排序
     * 　　a、默认从第二个数据开始比较。
     * 　　b、如果第二个数据比第一个小，则交换。然后在用第三个数据比较，如果比前面小，则插入（狡猾）。否则，退出循环
     * 　　c、说明：默认将第一数据看成有序列表，后面无序的列表循环每一个数据，如果比前面的数据小则插入（交换）。否则退出。
     */
    public static int[] insertSort(int[] arr) {
        for (int i = 1; i < arr.length; i++) {
            for (int j = i; j > 0; j--) {
                //内存循环，与前面排好序的数据比较，如果后面的数据小于前面的则交换
                if (arr[j] < arr[j - 1]) {
                    int temp = arr[j - 1];
                    arr[j - 1] = arr[j];
                    arr[j] = temp;
                } else {
                    //如果不小于，说明插入完毕，退出内层循环
                    break;
                }
            }

        }
        return arr;
    }

    /**
     *  希尔排序（插入排序变种版）
     *     a、基本上和插入排序一样的道理
     * 　　b、不一样的地方在于，每次循环的步长，通过减半的方式来实现
     * 　　c、说明：基本原理和插入排序类似，不一样的地方在于。通过间隔多个数据来进行插入排序。
     */
    public static int[] shellsSort(int[] arr) {
        for (int i = arr.length /2 ;i>0; i/=2){
            for (int j = i; j < arr.length; j++){
                for (int k = j; k > 0  && k - i >= 0; k -= i) {
                    if (arr[k] < arr[k - i]) {
                        int temp = arr[k - i];
                        arr[k - i] = arr[k];
                        arr[k] = temp;
                    } else {
                        break;
                    }
                }
            }
        }
        return arr;
    }

    /**
     *  快速排序
     *  　　a、确认列表第一个数据为中间值，第一个值看成空缺（低指针空缺）。
     * 　　b、然后在剩下的队列中，看成有左右两个指针（高低）。
     * 　　c、开始高指针向左移动，如果遇到小于中间值的数据，则将这个数据赋值到低指针空缺，并且将高指针的数据看成空缺值（高指针空缺）。然后先向右移动一下低指针，并且切换低指针移动。
     * 　　d、当低指针移动到大于中间值的时候，赋值到高指针空缺的地方。然后先高指针向左移动，并且切换高指针移动。重复c、d操作。
     * 　　e、直到高指针和低指针相等时退出，并且将中间值赋值给对应指针位置。
     * 　　f、然后将中间值的左右两边看成行的列表，进行快速排序操作。
     */
    private static int[] quickSort(int[] arr,int low, int hight){
        if (hight - low < 1) {
            return arr;
        }
        //标记，从高指针开始，还是低指针（默认高指针）
        boolean flag = true;
        int start = low;
        int end = hight;
        int midVal = arr[low];
        while (true){
            if (flag){
                if(arr[hight] > midVal){
                    hight --;
                }else if(arr[hight] < midVal){
                    arr[low] = arr[hight];
                    low ++;
                    flag = false;
                }
            }else {
                if(arr[low] < midVal){
                    low ++;
                }else if(arr[low] >midVal){
                    arr[hight] = arr[low];
                    hight--;
                    flag = true;
                }
            }
            if(low == hight){
                arr[low] = midVal;
                break;
            }
            //printArr(arr);
        }
        //然后出现有，中间值左边的小于中间值。右边的大于中间值。然后在对左右两边的列表在进行快速排序
        quickSort(arr, start, low -1);
        quickSort(arr, low + 1, end);

        return arr;
    }

    /**
     *
     */
    private static int[] mergeSort(int[] arr,int start,int end){

        if(end - start > 0){
            //再一次拆分，知道拆成一个一个的数据
            mergeSort(arr, start, (start + end) / 2);
            mergeSort(arr, (start + end) / 2 + 1, end);
            //记录开始/结束位置
            int left = start;
            int right = (start + end) / 2 + 1;
            //记录每个小单位的排序结果
            int index = 0;
            int[] result = new int[end - start + 1];
            //如果查分后的两块数据，都还存在
            while (left <= (start + end) / 2 && right <= end) {
                //比较两块数据的大小，然后赋值，并且移动下标
                if (arr[left] <= arr[right]) {
                    result[index] = arr[left];
                    left++;
                } else {
                    result[index] = arr[right];
                    right++;
                }
                //移动单位记录的下标
                index++;
            }
            //当某一块数据不存在了时
            while (left <= (start + end) / 2 || right <= end) {
                //直接赋值到记录下标
                if (left <= (start + end) / 2) {
                    result[index] = arr[left];
                    left++;
                } else {
                    result[index] = arr[right];
                    right++;
                }
                index++;
            }
            //最后将新的数据赋值给原来的列表，并且是对应分块后的下标。
            for (int i = start; i <= end; i++) {
                arr[i] = result[i - start];
            }

        }

        return arr;
    }

    private static void printArr(int[] arr){
        for (int i : arr) {
            System.out.print(i + " ");
        }
        System.out.println("");
    }
}
